﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using TrainGame.Model;

namespace TrainGame.AI.TileLay
{
	/// <summary>
	/// base class for tile lay AI goals
	/// </summary>
	internal abstract class TileLayGoal : AiGoal
	{
		/// <summary>
		/// What is this goal's "need" value - how important is serving this goal?
		/// </summary>
		/// <param name="extraInfo">extra parameters unique to the type of goal</param>
		/// <returns>value of this goal's 'need'</returns>
		public override int ComputeNeedValue(params object[] extraInfo)
		{
			Gameboard gameboard = GetParameter<Gameboard>(extraInfo);
			PublicCompany company = GetParameter<PublicCompany>(extraInfo);
			AiMoveConfig config = GetParameter<AiMoveConfig>(extraInfo);
			Dictionary<string, GameboardHex> runToHexes = GetParameter<Dictionary<string, GameboardHex>>(extraInfo);

			return ComputeNeed(gameboard, company, runToHexes, config);
		}

		/// <summary>
		/// derived classes must implement
		/// </summary>
		abstract protected int ComputeNeed(Gameboard gameboard, PublicCompany company, Dictionary<string, GameboardHex> runToHexes, AiMoveConfig config);

		/// <summary>
		/// Determine the best move for this goal.
		/// </summary>
		/// <param name="watch">a running stopwatch</param>
		/// <param name="timeLimit">if timelimit exceeded, return immediately</param>
		/// <param name="extraInfo">extra parameters unique to the type of goal</param>
		/// <returns>a list of the AI's best moves (caller can choose which to apply, if more than one)</returns>
		public override List<AiMove> ComputeMove(Stopwatch watch, TimeSpan timeLimit, params object[] extraInfo)
		{
			Gameboard origGameboard = GetParameter<Gameboard>(extraInfo);
			PublicCompany company = GetParameter<PublicCompany>(extraInfo);
			List<AiTileLay> tileLays = GetParameter<List<AiTileLay>>(extraInfo);
			Dictionary<string, GameboardHex> runToHexes = GetParameter<Dictionary<string, GameboardHex>>(extraInfo, 1);
			Dictionary<string, GameboardHex> tileLayHexes = GetParameter<Dictionary<string, GameboardHex>>(extraInfo, 2);
			AiMoveConfig config = GetParameter<AiMoveConfig>(extraInfo);

			// make copy of game board tiles and hexes, so we can modify it without affecting the real game board
			Gameboard gameboard = origGameboard.MakeCopy();
			origGameboard = null;	// lose the reference to real gameboard so we don't accidentally modify it

			List<AiTileLay> results = ComputeTileLays(watch, timeLimit, gameboard, company, tileLays, runToHexes, tileLayHexes, config);
			if (results == null)
				return null;
			return new List<AiMove>(results);
		}

		/// <summary>
		/// derived classes must implement
		/// </summary>
		/// <remarks>
		/// Derived classes are expected to watch the stopwatch and abort processing if the stopwatch runs out.
		/// </remarks>
		abstract internal List<AiTileLay> ComputeTileLays(System.Diagnostics.Stopwatch watch, TimeSpan timeLimit,
			Gameboard gameboard, PublicCompany company, List<AiTileLay> tileLays,
			Dictionary<string, GameboardHex> runToHexes, Dictionary<string, GameboardHex> tileLayHexes,
			AiMoveConfig config);
	}
}
